/* $Id$
 *
 * This file contains some useful functions for experimenting wiht
 * various aspects of Firefox via the mozrepl interactive javascript
 * shell.
 */

// {{{Imports 

var acc={};
repl.load("chrome://mozlab/content/mozremote/services/browser/accessibility_util.js", acc);

var util ={};
repl.load("chrome://mozlab/content/mozremote/util.js", util);

// }}}
// {{{ Firemacs setup

var fmName = "Firemacs-Window";

var mw = util.getWindowByName(fmName);
var tb = mw.getBrowser(); 
// }}}


// {{{ Frames example 6

var b1 = tb.browsers[0];
b1.loadURI("file:///home/luke/projects/jslisp/firemacs/tests/html/frames6/index.html");

var win = b1.contentWindow;

var link1= win.frames[0].document.getElementById("link1");
var link2=win.frames[0].document.getElementById("link2");
var link3=win.frames[0].document.getElementById("link3");

var accLink1 = acc.getAccessibleFor(link1);
var accLink2 = acc.getAccessibleFor(link2);
var accLink3 = acc.getAccessibleFor(link3);

// }}}
// {{{window load handler 

var lastTarget = null, lastOriginalTarget = null, lastCurrentTarget = null, lastExplicitOriginaltarget = null;
var lastEvent = null;


function myHandler(event) {
	repl.print(event.type + " event with target " + event.target + " " + event.originalTarget);
	/*
	if (getBrowser().contentDocument == event.originalTarget) {
		repl.print("This is the one.");
	}
	*/
	lastTarget = event.target;
	lastCurrentTarget = event.currentTarget;
	lastOriginalTarget = event.originalTarget;
	lastExplicitOriginaltarget = event.explicitOriginalTarget;

}


window.addEventListener("load", myHandler, true);
getBrowser().addEventListener("load", myHandler, true);


window.addEventListener("abort", myHandler, true);

window.addEventListener("error", myHandler, true);

window.removeEventListener("load", myHandler, true);

// }}}
// {{{Tab open handler

var myTabOpenHandler = function (event) {
	repl.print("bla");
	repl.print("Event: " + event.type);
	//repl.print("Browser: " + event.target.getBrowserForTab(event.originalTarget));
};

tb.addEventListener("TabOpen",myTabOpenHandler, true);
tb.addEventListener("TabClose",myTabOpenHandler, true);

tb.removeEventListener("TabOpen",myTabOpenHandler, true);

// }}}
// {{{WebProgressListener 

var Ci = Components.interfaces;
var nsIWebProgressListener = Ci.nsIWebProgressListener;
var nsIWebProgress = Ci.nsIWebProgress;

var myProgress = {
	
	// nsISupports
	QueryInterface: function(aIID) {
		if (aIID.equals(Components.interfaces.nsIWebProgressListener)   ||
			aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
			aIID.equals(Components.interfaces.nsISupports))
			return this;
		throw Components.results.NS_NOINTERFACE;
	},

	// nsIWebProgressListener
	onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) {
		// See below for real implementation
		repl.print("webProgressListener: onStateChange dummy implementation.");
	},

	onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress,
                                  aMaxTotalProgress) {
	},

	onLocationChange: function(aWebProgress, aRequest, aLocation) {
	},

	onStatusChange: function(aWebProgress, aRequest, aStatus, aMessage) {
	},

	onSecurityChange: function(aWebProgress, aRequest, state) {
	}

};

tb.browsers[1].webProgress.addProgressListener(myProgress, -1);
getBrowser().selectedBrowser.webProgress.removeProgressListener(myProgress);

// this is the real implementation
myProgress.onStateChange= function(aWebProgress, aRequest, aStateFlags, aStatus) {
	var msg = "";
	try {
		if (aStateFlags & nsIWebProgressListener.STATE_START) {
			msg += "Start event ";
		} else  		if (aStateFlags & nsIWebProgressListener.STATE_REDIRECTING) {
			msg += "Redirect event ";
		} else 		if (aStateFlags & nsIWebProgressListener.STATE_TRANSFERRING) {
			msg += "Transferring event ";
		} else 		if (aStateFlags & nsIWebProgressListener.STATE_STOP) {
			msg += "Stop event ";
		} else {
			msg += "Unknown event ";
		}

		// Now check other flags
		var flags = [ "IS_request", "IS_document", "IS_window", "IS_network", "RESTORING" ];
		msg += "(";
		for each (var flag in flags) {
			if (aStateFlags & nsIWebProgressListener["STATE_" + flag.toUpperCase()]) {
				msg += flag + " ";
			} 
		}
		msg += ") ";
		if (aStatus) {
			msg += "Status: 0x" + aStatus.toString(16);
		}
		msg += " " + aRequest.name;
	} catch (e) {
		repl.print(e);
	}
	repl.timedPrint(msg);		
};

myProgress.onLocationChange = function(aWebProgress, aRequest, aLocation) {
	repl.print("Location change: " + (aRequest == null ? "page internal link " : "") +aLocation.spec);
};

var urlStr = aRequest.QueryInterface(nsIChannel).URI.spec;
var urlStr = aRequest.QueryInterface(nsIChannel).originalURI.spec;
notification = Components.isSuccessCode(aStatus) ? "EndDocumentLoad" : "FailDocumentLoad";

myProgress.onStatusChange= function(aWebProgress, aRequest, aStatus, aMessage) {
	repl.print("Status change: " + aMessage);
};

// }}}
// {{{Enumerating HTML documents 

// Enumerating windows
function findAllHtmlDocuments() {
	var ifaces = Components.interfaces;

	var mediator = Components.classes["@mozilla.org/appshell/window-mediator;1"].
		getService(ifaces.nsIWindowMediator);

	var winEnum = mediator.getXULWindowEnumerator(null);

	while (winEnum.hasMoreElements()){
		var win = winEnum.getNext();

		if(!(win instanceof ifaces.nsIXULWindow)) {
			continue;
		}

		var iface = ifaces.nsIDocShellTreeItem;
		var docShellEnum = win.docShell.getDocShellEnumerator(
															  iface.typeAll, iface.ENUMERATE_FORWARDS);

		repl.print("Window: (", false);

		while(docShellEnum.hasMoreElements()) {
			var docShell = docShellEnum.getNext();
			if(docShell instanceof ifaces.nsIDocShell) {
				var domDocument = docShell.contentViewer.DOMDocument;
				if(domDocument instanceof HTMLDocument) {
					repl.print("\n\t"+docShell,  false);
					repl.print("\t" + domDocument, false);
					repl.print("\t" + domDocument.title, false);
				}
			}
		}
		repl.print("\n)\n", false);
	}
}

// }}}
// {{{windowMediatorListener 

var windowListener = {
	onCloseWindow: function (win) {
		repl.print("Window closed: " + win);
	},
	onOpenWindow: function (win) {
		repl.print("A new window just opened: " + win);
	},
	onWindowTitleChange: function (win, newTitle) {}
};
util.service("@mozilla.org/appshell/window-mediator;1", "nsIWindowMediator")
		.addListener(windowListener);

// }}}
// {{{Observers

var CI = Components.interfaces;
var CR = Components.results;

var observerService = Components.classes["@mozilla.org/observer-service;1"]
	.getService(Components.interfaces.nsIObserverService);

// {{{Load observer 

var myLoadObserver = {
	// nsIObserver
	observe: function (aSubject, aTopic, aData) {
		repl.print("Load observer dummy implementation.");
	},

	// nsISupports
	QueryInterface: function(aIID) {
		if(!aIID.equals(CI.nsISupports) && !aIID.equals(CI.nsIObserver)) {
			throw CR.NS_ERROR_NO_INTERFACE;
		}
		return this;
	}
};

// this is the real implementation
myLoadObserver.observe = function (aSubject, aTopic, aData) {
	repl.print("Load observer: " + aTopic + " " + aData + " " + aSubject);

			/*
		  switch (aTopic) {
		  case "StartDocumentLoad":
		  repl.print("StartDocumentLoad: " + data);
		  break;
		  case "EndDocumentLoad":
		  repl.print("EndDocumentLoad: " + data);
		  break;
		  case "FailDocumentLoad":
		  repl.print("FailDocumentLoad: " + data);
		  break;
		  }
		*/
};

observerService.addObserver(myLoadObserver, "StartDocumentLoad", false);
observerService.addObserver(myLoadObserver, "EndDocumentLoad", false);
observerService.addObserver(myLoadObserver, "FailDocumentLoad", false);

observerService.removeObserver(myLoadObserver, "StartDocumentLoad");
observerService.removeObserver(myLoadObserver, "EndDocumentLoad");
observerService.removeObserver(myLoadObserver, "FailDocumentLoad");


// }}}
// {{{Accessibility observer

var IAccEventNames = util.initConstantNames(CI.nsIAccessibleEvent, "EVENT_");
var myAccObserver = {
	// nsIObserver
	observe: function (aSubject, aTopic, aData) {
		// this is only a placeholder, real implementation below.
		repl.print("Accessibility observer: Dummy implementation");
},

	// nsISupports
	QueryInterface: function(aIID) {
		if(!aIID.equals(CI.nsISupports) && !aIID.equals(CI.nsIObserver)) {
			throw CR.NS_ERROR_NO_INTERFACE;
		}
		return this;
	}
};

// this is the real implementation
myAccObserver.observe = function (aSubject, aTopic, aData) {
	repl.print("Accessibility observer:  aTopic=" + aTopic + " aData=" + aData + " aSubject=" + aSubject);
	var av;
	try {
		av = aSubject.QueryInterface(CI.nsIAccessibleEvent);
	} catch (e) {
		repl.print(e);
		return;
	}
	repl.print(IAccEventNames[av.eventType]);
	var accessible = av.accessible;
	if (accessible) {
		dumpAccessibleNode(accessible);
	} else {
		repl.print("No nsIAccessible for this event.");
	}
	var domNode = av.DOMNode;
	if (domNode) {
		repl.print("node.nodeName: " + domNode.nodeName);
	} else {
		repl.print("No nsIDOMNode for this event.");
	}
};

observerService.addObserver(myAccObserver, "accessible-event", false);
observerService.removeObserver(myAccObserver, "accessible-event");

// }}}

// }}}
// {{{Dumping information on the accessible tree

var CI = Components.interfaces;
var IAccRoleNames = util.initConstantNames(acc.nsIAccessibleRole, "ROLE_");
var IAccStateNames = util.initConstantNames(acc.nsIAccessibleStates, "STATE_");
var IAccExtStateNames = util.initConstantNames(acc.nsIAccessibleStates, "EXT_STATE_");
var IAccRelationNames = util.initConstantNames(CI.nsIAccessible, "RELATION_");

function listActionNames(aNode) {
	var result = [];
	for (var i = 0; i < aNode.numActions; ++i) {
		result.push(aNode.getActionName(i));
	}
	return result;
}


function dumpAccessibleNode(aNode, level) {
	var msg = "";
	
	var indent = "level " + level + " ";
	msg += indent;

msg += "role=\"" + IAccRoleNames[aNode.finalRole] + "\" ";
	try {
		msg += "name=\"" + aNode.name + "\" ";
	} catch (e) {
		msg += " noName ";
	}
	try {
		var value = aNode.finalValue;
		if (value) {
			msg += "finalValue=\"" + value + "\" ";
		}
	} catch (e) {
	}
	var [state, extState] = acc.getStates(aNode);
	msg += "state=\"" + util.checkedFlagNames(IAccStateNames, state) + "\" ";
	msg += "extState=\"" + util.checkedFlagNames(IAccExtStateNames, extState) + "\" ";
	try {
		var desc = aNode.description;
		if (desc) {
			msg += "description=\"" + desc + "\" ";
		}
	} catch (e) {
	}
	if (aNode.numActions > 0) {
		msg += "actions=\"" + listActionNames(aNode).join(", ") + "\" ";
	}
	// msg += "relation=\"" + IAccRelationNames[aNode.relation] + "\" ";
	if (aNode.role == CI.nsIAccessible.ROLE_TABLE) {
		var accTable = aNode.QueryInterface(CI.nsIAccessibleTable);
		
	}

	repl.print(msg);
}


function dumpAccessibleTree(aNode, level) {
	level = level || 0;
	// aNode is an nsIAccessible

	dumpAccessibleNode(aNode, level);
	try {	
		var child = aNode.firstChild;
		while (child) {
			dumpAccessibleTree(child, level + 1);
			child = child.nextSibling;
		}
	} catch (e) {
		repl.print("Error visiting child nodes: " + e);
	}
}

// }}}
// {{{Event dispatch simulation 

function simulateClick(target, doc) {
  var evt = doc.createEvent("MouseEvents");
  evt.initMouseEvent("click", true, true, doc.defaultView,
					 0, 0, 0, 0, 0, false, false, false, false, 0, null);
  
  var canceled = !target.dispatchEvent(evt);

  return canceled;
}

	// }}}
// {{{change event for forms 

function onChangeHandler(event) {
	var accTarget = acc.getAccessibleFor(event.target);
	var accRelated = null;
	if (event.relatedTarget) {
		accRelated = acc.getAccessibleFor(event.relatedTarget);
	}
	repl.print("Change event on: \"" + event.target + "\" " + (accRelated ? " \"" + accRelated.name  + "\"": " no relatedTarget"));
}

doc.addEventListener("change", onChangeHandler, true);

doc.removeEventListener("change", onChangeHandler, true);


// }}}
// {{{DOM modification events

function myDOMEventHandler(event) {
	repl.print("Event: " + event.type + " " + event.target);
}


function addEventListeners(target, handler, events, capture) {
	for each (var event in events) {
		target.addEventListener(event, handler, capture);
	}
}

function removeEventListeners(target, handler, events, capture) {
	for each (var event in events) {
		target.removeEventListener(event, handler, true, capture);
	}
}


var docEvents = [
  // capture DOM focus events 
  "focus",
  // capture Form change events 
  "select",
  "change",
  // capture NameChange events (fired whenever name changes, immediately after, whether focus moves or not)
  "NameChange",
  // capture ValueChange events (fired whenever value changes, immediately after, whether focus moves or not)
  "ValueChange",
  // capture AlertActive events (fired whenever alert pops up)
  "AlertActive",
  // add ourself as a TreeViewChanged listener (custom event fired in nsTreeBodyFrame.cpp)
  "TreeViewChanged",
  // add ourself as a OpenStateChange listener (custom event fired in tree.xml)
  "OpenStateChange",
  // add ourself as a CheckboxStateChange listener (custom event fired in nsHTMLInputElement.cpp)
  "CheckboxStateChange",
  // add ourself as a RadioStateChange Listener ( custom event fired in in nsHTMLInputElement.cpp  & radio.xml)
  "RadioStateChange",
  "popupshown",
  "popuphiding",
  "DOMMenuInactive",
  "DOMMenuItemActive",
  "DOMMenuBarActive",
  "DOMMenuBarInactive",
  "DOMContentLoaded",
  "DOMNodeInserted",
  "DOMNodeRemoved",
  "DOMNodeInsertedIntoDocument",
  "DOMNodeRemovedFromDocument",
  "DOMAttrModified",
  "DOMSubTreeModified",
  "DOMCharacterDataModified"
];

addEventListeners(content.document, myDOMEventHandler, docEvents, true);

removeEventListeners(content.document, myDOMEventHandler, docEvents, true);
// }}}

// {{{charset

function charCodes(aString) {
	var a = [];
	for (var i = 0; i < aString.length; ++i) {
		a.push(aString.charCodeAt(i));
	}
	return a;
}

// }}}
// {{{Session history listener

function SessionHistoryListener  () {

}

SessionHistoryListener.prototype = {
	// Implementation of nsISupports

	_interfaces: [ Components.interfaces.nsISHistoryListener ],
	QueryInterface: function(aIID) {
		if (this._interfaces.some(function (aID) { return aIID.equals(aID); }) ||
			aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
			aIID.equals(Components.interfaces.nsISupports))
		return this;
		throw Components.results.NS_NOINTERFACE;
	},

	// Implementation of nsISHistoryListener

    OnHistoryNewEntry: function(newUrl)
    {
       uriSpec = newUrl.spec;

       repl.print("In OnHistoryNewEntry(). uriSpec = " + uriSpec);

	   return true;
    },

	OnHistoryGoBack: function(uri) {
       var uriSpec = uri.spec;

       repl.print("In OnHistoryGoBack(). uriSpec = " + uriSpec);
       return true;
    },

    OnHistoryGoForward: function(uri) {
       var uriSpec = uri.spec;

       repl.print("In OnHistoryGoForward(). uriSpec = " + uriSpec);

       return true;
    },

    OnHistoryReload: function(uri, reloadFlags)
    {
       uriSpec = uri.spec;

       repl.print("In OnHistoryReload(). uriSpec = " + uriSpec + " reloadFlags: " + reloadFlags);

       return true;
    },

    OnHistoryGotoIndex: function(index, uri)
    {
       uriSpec = uri.spec;

       repl.print("In OnHistoryGotoIndex(). uriSpec = " + uriSpec + " index=" + index);


       return true;
    },

    OnHistoryPurge: function(numEntries)
    {
       repl.print("In OnHistoryPurge(). numEntries = " + numEntries);

       return true;
    }
};

function listProperties(obj, propList) {
	var msg = [];
	for each (prop in propList) {
		msg.push(prop + ":" + obj[prop])
			}
	return msg.join(" ");
}

function printSHistoryEntry(entry, level) {
	var level = level || 0;
	var msg = (level) ? "level " + level + " " : "";
	repl.print(msg + listProperties(entry, [ "title", "isSubFrame"]) + " " + listProperties(entry, ["ID", "pageIdentifier" ]) + " URI:" + entry.URI.spec);
	for (var i = 0; i < entry.childCount; ++i) {
		printSHistoryEntry(entry.GetChildAt(i), level + 1);
	}
}

function printSHistory(browser) {
	var sh = browser.webNavigation.sessionHistory;

	var shEnum = sh.SHistoryEnumerator;

	while (shEnum.hasMoreElements()) {
		var sessionHistoryEntry = shEnum.getNext();
		var SHEntry = sessionHistoryEntry.QueryInterface(Ci.nsISHEntry);

		printSHistoryEntry(SHEntry, 0);
	}
}

// }}}

// {{{ Emacs local variables

// local variables:
// folded-file: t
// mode: moz-minor
// end:

// }}}

